// Copyright Epic Games, Inc. All Rights Reserved.

#include "zenhttp/httptest.h"

#include <zencore/compactbinarybuilder.h>
#include <zencore/compactbinarypackage.h>
#include <zencore/fmtutils.h>
#include <zencore/timer.h>

#if ZEN_WITH_TESTS

namespace zen {

using namespace std::literals;

HttpTestingService::HttpTestingService()
{
	m_Router.RegisterRoute(
		"hello",
		[](HttpRouterRequest& Req) { Req.ServerRequest().WriteResponse(HttpResponseCode::OK); },
		HttpVerb::kGet);

	m_Router.RegisterRoute(
		"hello_slow",
		[](HttpRouterRequest& Req) {
			Req.ServerRequest().WriteResponseAsync([](HttpServerRequest& Request) {
				Stopwatch Timer;
				Sleep(1000);
				Request.WriteResponse(HttpResponseCode::OK,
									  HttpContentType::kText,
									  fmt::format("hello, took me {}", NiceTimeSpanMs(Timer.GetElapsedTimeMs())));
			});
		},
		HttpVerb::kGet);

	m_Router.RegisterRoute(
		"hello_veryslow",
		[](HttpRouterRequest& Req) {
			Req.ServerRequest().WriteResponseAsync([](HttpServerRequest& Request) {
				Stopwatch Timer;
				Sleep(60000);
				Request.WriteResponse(HttpResponseCode::OK,
									  HttpContentType::kText,
									  fmt::format("hello, took me {}", NiceTimeSpanMs(Timer.GetElapsedTimeMs())));
			});
		},
		HttpVerb::kGet);

	m_Router.RegisterRoute(
		"hello_throw",
		[](HttpRouterRequest& Req) {
			Req.ServerRequest().WriteResponseAsync([](HttpServerRequest&) { throw std::runtime_error("intentional error"); });
		},
		HttpVerb::kGet);

	m_Router.RegisterRoute(
		"hello_noresponse",
		[](HttpRouterRequest& Req) { Req.ServerRequest().WriteResponseAsync([](HttpServerRequest&) {}); },
		HttpVerb::kGet);

	m_Router.RegisterRoute(
		"metrics",
		[this](HttpRouterRequest& Req) {
			metrics::OperationTiming::Scope _(m_TimingStats);
			Req.ServerRequest().WriteResponse(HttpResponseCode::OK);
		},
		HttpVerb::kGet);

	m_Router.RegisterRoute(
		"get_metrics",
		[this](HttpRouterRequest& Req) {
			CbObjectWriter Cbo;
			EmitSnapshot("requests", m_TimingStats, Cbo);
			Req.ServerRequest().WriteResponse(HttpResponseCode::OK, Cbo.Save());
		},
		HttpVerb::kGet);

	m_Router.RegisterRoute(
		"json",
		[this](HttpRouterRequest& Req) {
			CbObjectWriter Obj;
			Obj.AddBool("ok", true);
			Obj.AddInteger("counter", ++m_Counter);
			Req.ServerRequest().WriteResponse(HttpResponseCode::OK, Obj.Save());
		},
		HttpVerb::kGet);

	m_Router.RegisterRoute(
		"echo",
		[](HttpRouterRequest& Req) {
			IoBuffer Body = Req.ServerRequest().ReadPayload();
			Req.ServerRequest().WriteResponse(HttpResponseCode::OK, HttpContentType::kBinary, Body);
		},
		HttpVerb::kPost);

	m_Router.RegisterRoute(
		"package",
		[](HttpRouterRequest& Req) {
			CbPackage Pkg = Req.ServerRequest().ReadPayloadPackage();
			Req.ServerRequest().WriteResponse(HttpResponseCode::OK, Pkg);
		},
		HttpVerb::kPost);
}

HttpTestingService::~HttpTestingService()
{
}

const char*
HttpTestingService::BaseUri() const
{
	return "/testing/";
}

void
HttpTestingService::HandleRequest(HttpServerRequest& Request)
{
	m_Router.HandleRequest(Request);
}

Ref<IHttpPackageHandler>
HttpTestingService::HandlePackageRequest(HttpServerRequest& HttpServiceRequest)
{
	RwLock::ExclusiveLockScope _(m_RwLock);

	const uint32_t RequestId = HttpServiceRequest.RequestId();

	if (auto It = m_HandlerMap.find(RequestId); It != m_HandlerMap.end())
	{
		Ref<HttpTestingService::PackageHandler> Handler = std::move(It->second);

		m_HandlerMap.erase(It);

		return Handler;
	}

	auto InsertResult = m_HandlerMap.insert({RequestId, Ref<PackageHandler>()});

	_.ReleaseNow();

	return (InsertResult.first->second = Ref<PackageHandler>(new PackageHandler(*this, RequestId)));
}

//////////////////////////////////////////////////////////////////////////

HttpTestingService::PackageHandler::PackageHandler(HttpTestingService& Svc, uint32_t RequestId) : m_Svc(Svc), m_RequestId(RequestId)
{
}

HttpTestingService::PackageHandler::~PackageHandler()
{
}

void
HttpTestingService::PackageHandler::FilterOffer(std::vector<IoHash>& OfferCids)
{
	ZEN_UNUSED(OfferCids);
	// No-op
	return;
}
void
HttpTestingService::PackageHandler::OnRequestBegin()
{
}

void
HttpTestingService::PackageHandler::OnRequestComplete()
{
}

IoBuffer
HttpTestingService::PackageHandler::CreateTarget(const IoHash& Cid, uint64_t StorageSize)
{
	ZEN_UNUSED(Cid);
	return IoBuffer{StorageSize};
}

}  // namespace zen

#endif
